import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.UnmarshalException;
import javax.xml.bind.Unmarshaller;

import com.sun.xml.internal.stream.writers.UTF8OutputStreamWriter;

import Streams.UTFInputStream;
import Streams.UTFOutputStream;
import Streams.XmlInStream;
import Streams.XmlOutStream;
import XSD.BoardType;
import XSD.ErrorType;
import XSD.LoginReplyMessageType;
import XSD.MazeCom;
import XSD.MazeComType;
import XSD.ObjectFactory;

public class Client {
	// einige wichtige variablen
	private int serverport;
	private String serverIP ;
	private String pname;
	private Socket clientSocket;
	private int ID;
	private int tries; //muss die evt statisch sein O.o???
	final int limitTries = 5;
	
	//streams
	private XmlOutStream toServer;
	private XmlInStream fromServer;
	
	//der Konstruktor
	//hier findet auch die Socket connection mit statt
	public Client(String serverIP, int serverPort, String pname)
	{
		this.serverIP=serverIP;
		this.serverport=serverPort;
		this.pname=pname;
		this.tries = 0;
		
		//socket erzeugen
		try {
			this.clientSocket = new Socket(this.serverIP, this.serverport);
			
			//outputStream zum Server
			this.toServer=new XmlOutStream(new DataOutputStream(this.clientSocket.getOutputStream()));
			//inputStream vom Server
			this.fromServer=new XmlInStream(new DataInputStream(this.clientSocket.getInputStream()));
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		//wichtige variablen
		int serverport = 5123;
		String serverIP = "localhost";
		String pname = "GA-GreenApe";
		boolean iStayConnected = true;
		
		//client erzeugen -> damit auch socket erzeugt
		Client cl=new Client(serverIP, serverport, pname);

		// Message Factory erzeugen
		MazeComMesFactory mcmf = new MazeComMesFactory();
		//TaW Object deklarieren
		TaW t = null;

		// Login Message ueber den Socket verschicken
		MazeCom message = mcmf.createLogin(cl.getPname());
		cl.toServer.write(message);
		
		
		// Reply Messages verarbeiten bis connect
		while(true)
		{
			MazeCom antwort=cl.fromServer.readMazeCom();
			if(antwort.getMcType().compareTo(MazeComType.LOGINREPLY) == 0)
			{
				LoginReplyMessageType lrmt = antwort.getLoginReplyMessage();
				// ID Sichern
				cl.setID(lrmt.getNewID());
				break;
			}
			//hier fehlerfaelle abfangen bzw. error messages aber aktuell nicht implementiert
			else 
			{
				if(antwort.getMcType().compareTo(MazeComType.DISCONNECT) == 0 && antwort.getDisconnectMessage().getErroCode().compareTo(ErrorType.AWAIT_LOGIN) == 0)
				{
					continue;
				}
				else
				{	
					System.out.println("Der Login ist fehlgeschlagen");
					break;
				}
			}
			
		} //hier steht die Verbindung mit dem Server (sobald die fehlerfaelle abgefangen sind ^^)

		// While schleife
		//********** AB HIER MUSS MYRI IMPLEMENTIEREN **********//
		while(iStayConnected)
		{
			MazeCom antwort = cl.fromServer.readMazeCom();
			
			switch(antwort.getMcType())
			{
				case LOGINREPLY:	
					/*hier ist etwas passiert was nicht haette passieren duerfen O.o */ 
					if(cl.clientSocket.isConnected())
					{
						System.out.println("LoginReply vom Server, aber Connection steht.\n weiter gehts!");
						continue;
					}
					else
					{
						System.out.println("Fehler vom Server. LoginReply kommt zu spaet.");
					}
					break;
					
				case AWAITMOVE:
					System.out.println(antwort.getAwaitMoveMessage().getBoard().toString());
					// einen Zug machen!
					t = new TaW(cl.getID(), antwort.getAwaitMoveMessage().getBoard(), cl.clientSocket);
					t.start();
					cl.incTries(); //anzahl versuche hochzaehlen!
					// TODO: bekommt er den rest die treasures und so nicht??? O.o
					break;
					
				case ACCEPT:
					// den bool checken ob der zug ok war.
					if(!antwort.getAcceptMessage().isAccept())
					{
							// --> pruefen was los ist!
							switch(antwort.getAcceptMessage().getErrorCode())
							{
							case NOERROR: 
								/*hier ist alles ok und kann ignoriert werden*/
								break;
								
							case ERROR: 
								System.out.println("Da ist etwas schief gegangen. O.o\nVerbindung wird getrennt.");
								cl.verbindung_beenden();
								iStayConnected = false;
								break;
								
							case AWAIT_LOGIN: 
								//hier an der stelle voellig falsch
								System.out.println("Server erwartet Login.");
								if(cl.clientSocket.isConnected()) //falls verbindung vorhanden wird sie getrennt!
								{
									cl.verbindung_beenden();
									iStayConnected = false;
								}
								break;
								
							case AWAIT_MOVE: 
								// einen Zug machen!
								t.writeMoveToServer(cl.getTries());
								cl.incTries(); //anzahl versuche hochzaehlen!
								// TODO: bekommt er den rest die treasures und so nicht??? O.o
								break;
								
							case ILLEGAL_MOVE: 
								if(cl.getTries() < cl.getLimitTries()) // wir duerfen noch mal versuchen! ^^
								{
									// einen Zug machen!
									t.writeMoveToServer(cl.getTries());
									cl.incTries(); //anzahl versuche hochzaehlen!
									// TODO: bekommt er den rest die treasures und so nicht??? O.o
								}
								else
								{
									//TODO was mache ich wenn wir nix mehr machen d[rfen? logout!!!
									cl.verbindung_beenden();
									iStayConnected = false;
								}
								
								break;
								
							case TIMEOUT: 
								//disconnect. da ist in der verbindung oder bei uns wohl was falsch O.o
								System.out.println("Timeout! Verbindung wird beendet");
								iStayConnected = false;
								cl.verbindung_beenden();
								break;
								
							case TOO_MANY_TRIES: 
								//TODO evt mal nachfragen xD
								/*ignorieren und einfach weiter senden, es wird schon ein fehler kommen*/
								System.out.println("Too Many Tries: "+ cl.getTries());
								break;
								
							default : 
								System.out.println("Etwas echt komisches ist passiert O.o");
								break;
							}
					}
					else
					{
						cl.clearTries(); //move accepted! ^^
					}
					break;
					
				case WIN: 
					System.out.println(antwort.getAwaitMoveMessage().getBoard().toString());
					System.out.println("Der Spieler " + antwort.getWinMessage().getWinner().getId() + " hat das Spiel gewonnen");
					System.out.println("Die Verbindung wird getrennt");
					cl.verbindung_beenden();
					iStayConnected = false;
					break;
					
				case DISCONNECT: 
					//wenn ich n disconnect bekomme --> einfach raushauen!
					iStayConnected = false;
					System.out.println("Server: Disconnect. Die Verbindung wird getrennt");
					cl.verbindung_beenden();
					break;
				default : 
					System.out.println("Etwas echt komisches ist passiert O.o");
					break;
			}
		}

		
		// Verbindung abbauen
		//cl.verbindung_beenden();
	}
	
	//gibt den namen des Spielers zurueck
	public String getPname()
	{
		return this.pname;
	}
	
	public int getID()
	{
		return this.ID;
	}
	
	public void setID(int id)
	{
		this.ID=id;
	}
	
	public int getLimitTries()
	{
		return limitTries;
	}
	
	public void clearTries()
	{
		tries = 0;
	}
	
	public void incTries()
	{
		tries++;
	}
	
	public int getTries()
	{
		return tries;
	}
	
	public void verbindung_beenden()
	{
		try {
			this.clientSocket.close();
		} catch (IOException e) {
			System.out.println("Fehler beim Schliessen der Verbindung");
		}
	}
}
